﻿// Copyright 2008 Chris Varley (http://www.contractdriven.net/)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using ContractDriven.Aspects;

namespace ContractDriven
{
	/// <summary>
	/// An abstract base class from which you can inherit classes that use contracts.
	/// This provides an implentation of IDispoable, is marked with an "Invariant" attribute.
	/// which "injects" and "Invariant Aspect" to check invariants after each method call,
	/// and wrapper methods for static Contract calls.
	/// </summary>
	[Invariant]
	public abstract class ContractBoundObject: ContextBoundObject, IDisposable
	{

		#region Require (Pre-Conditions)
		
		/// <summary>
		/// Guarantees a pre-condition. Throws a ContractException if the condition evaluates to false.
		/// </summary>
		/// <param name="assertion">A condition that evaluates to true or false.</param>
		public void Require(bool assertion)
		{
            Contract.Require(assertion);
		}

		/// <summary>
		/// Guarantees a pre-condition. Throws a ContractException if the condition evaluates to false.
		/// </summary>
		/// <param name="assertion">A condition that evaluates to true or false.</param>
		/// <param name="description">An exception message to display if false.</param>
		public void Require(bool assertion, string description)
		{
            Contract.Require(assertion, description);	
		}


		/// <summary>
		/// Guarantees a pre-condition. Throws a ContractException if the condition evaluates to false.
		/// </summary>
		/// <param name="assertion">A BaseAssertion, that, via it's Eval() method returns 'true', if the assertion is true, 'false' if the assertion is false.</param>
        public void Require(IAssertion assertion)
		{
            Contract.Require(assertion);
		}

		#endregion

		#region Ensure (Post-Conditions)
		
		/// <summary>
		/// Guarantees a post-condition. Throws a ContractException if the condition evaluates to false.
		/// </summary>
		/// <param name="assertion">A condition that evaluates to true or false.</param>
		public void Ensure(bool assertion)
		{
            Contract.Ensure(assertion);
		}

		/// <summary>
		/// Guarantees a pre-condition. Throws a ContractException if the condition evaluates to false.
		/// </summary>
		/// <param name="assertion">A condition that evaluates to true or false.</param>
		/// <param name="description">An exception message to display if false.</param>
		public void Ensure(bool assertion, string description)
		{
			Contract.Ensure(assertion, description);
		}

		/// <summary>
		/// Guarantees a pre-condition. Throws a ContractException if the condition evaluates to false.
		/// </summary>
		/// <param name="assertion">A BaseAssertion, that, via it's Eval() method returns 'true', if the assertion is true, 'false' if the assertion is false.</param>
		public void Ensure(IAssertion assertion)
		{
			Contract.Ensure(assertion);
		}

		#endregion

		#region Invariant (Class Validation)

		/// <summary>
		/// Guarantees an invariant state.
		/// </summary>
		/// <param name="assertion">A delegate for a condition that evaluates to true or false.</param>
		/// <param name="description"></param>
		public void Invariant(Contract.AssertionStatementDelegate assertion, string description)
		{
			Contract.Invariant(this, assertion, description);
		}

		/// <summary>
		/// Guarantees an invariant state.
		/// </summary>
		/// <param name="assertion">A delegate for a BaseAssertion, that, via it's Eval() method returns 'true', if the assertion is true, 'false' if the assertion is false.</param>
		public void Invariant(Contract.AssertionObjectDelegate assertion)
		{
			Contract.Invariant(this, assertion);
		}

		#endregion

		#region Side Effect Checks

		/// <summary>
		/// Tracks a value for later comparison.
		/// </summary>
		/// <param name="value">The value to track.</param>
		/// <returns>A globally unique identifier (GUID) to retrive the item for comparison.</returns>
		public Guid Track(ValueType value)
		{
            return Contract.Track(value);
		}

		/// <summary>
		/// <para>Retrieves a previously tracked value by it's globally unique identifier (GUID)</para>
		/// </summary>
		/// <param name="key">the GUID returned from a call to Track(value)</param>
		/// <returns>The previous value</returns>
		public ValueType Before(Guid key)
		{
            return Contract.Before(key);
		}

		#endregion

		#region IDisposable Implementation

		~ContractBoundObject()
        {
        	Dispose(false);
        }

		public void Dispose()
		{
        	Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				// free the state of any contained objects here
				// (in this case we don't contain any other objects so no freeing required) 
			}
			// free my own state 
			Contract.Unload(this);
		}

		#endregion

	}
}
